﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using TNet;

public class SPVPLobbyClient
{
    public string errorString;
    private const long INVITE_TIME = 0x7d0L;
    private TNet.List<string> ipList;
    public bool isActive;
    public ServerList knownServers;
    private const int MAX_INVITESEND_TIMES = 8;
    private TNet.Buffer mBuffer;
    private ServerList.Entry mCurGS;
    private IPEndPoint mCurInviteIP;
    private int mCurInviteSendTimes;
    private string mCurKey;
    private TNet.Buffer mLookupIPRequest;
    private long mNextInviteSend;
    private long mNextSend;
    private bool mReEnable;
    private TNet.List<IPEndPoint> mRemoteAddressList;
    private int mScanEndPort;
    private int mScanStartPort;
    private IPEndPoint mSkipIPEP;
    private UdpProtocol mUdp;
    public OnListChange onChange;
    public OnResponseJoin onResponseJoin;
    private readonly long SCAN_TIME;

    public SPVPLobbyClient()
    {
        this.errorString = string.Empty;
        this.knownServers = new ServerList();
        this.mScanStartPort = 0x112f;
        this.mScanEndPort = 0x1139;
        this.SCAN_TIME = 0xbb8L;
        this.mUdp = new UdpProtocol();
        this.mRemoteAddressList = new TNet.List<IPEndPoint>();
        this.mCurKey = string.Empty;
    }

    public SPVPLobbyClient(int startPort, int endPort, string localIP = null, int localPort = 0, TNet.List<string> ipList = null, long scanTime = 0)
    {
        this.errorString = string.Empty;
        this.knownServers = new ServerList();
        this.mScanStartPort = 0x112f;
        this.mScanEndPort = 0x1139;
        this.SCAN_TIME = 0xbb8L;
        this.mUdp = new UdpProtocol();
        this.mRemoteAddressList = new TNet.List<IPEndPoint>();
        this.mCurKey = string.Empty;
        if ((localIP != null) && (localPort != 0))
        {
            this.mSkipIPEP = new IPEndPoint(IPAddress.Parse(localIP), localPort);
        }
        if ((ipList != null) && (ipList.Count > 0))
        {
            TNet.List<string> list = ipList;
            lock (list)
            {
                this.ipList = ipList;
            }
        }
        if (scanTime != 0)
        {
            this.SCAN_TIME = scanTime;
        }
        this.SetScanPortRange(startPort, endPort);
    }

    public void AddIpToRemoteList(string ip)
    {
        TNet.List<string> ipList = this.ipList;
        lock (ipList)
        {
            this.ipList.Add(ip);
        }
        this.ReBuildScanList();
    }

    public void ApplicationPause(bool paused)
    {
        if (paused)
        {
            if (this.isActive)
            {
                this.mReEnable = true;
                this.Stop();
            }
        }
        else if (this.mReEnable)
        {
            this.mReEnable = false;
            this.Start();
        }
    }

    protected BinaryWriter BeginSend(SPacket packet)
    {
        this.mBuffer = TNet.Buffer.Create();
        return this.mBuffer.BeginPacket((byte) packet);
    }

    private void DoAddToAddressList(int curRemotePort, IPEndPoint curRemoteAddress)
    {
        if (curRemoteAddress == null)
        {
            this.mUdp.Error(new IPEndPoint(IPAddress.Loopback, this.mUdp.listeningPort), "Invalid Port: " + curRemotePort);
        }
        else
        {
            this.mRemoteAddressList.Add(curRemoteAddress);
        }
    }

    protected void EndSend(IPEndPoint ip)
    {
        this.mBuffer.EndPacket();
        this.mUdp.Send(this.mBuffer, ip);
        this.mBuffer.Recycle();
        this.mBuffer = null;
    }

    public void InviteIPJoin(IPEndPoint inviteIp, ServerList.Entry gsEnt, string key = "")
    {
        this.mCurInviteIP = inviteIp;
        this.mCurGS = gsEnt;
        this.mCurKey = key;
        this.mCurInviteSendTimes = 0;
        this.mNextInviteSend = 0L;
    }

    public void InviteIPJoin(IPEndPoint inviteIp, string gsName, IPEndPoint gsIP, int playerCount = 0, string key = "")
    {
        ServerList.Entry gsEnt = new ServerList.Entry {
            name = gsName,
            internalAddress = gsIP,
            externalAddress = gsIP,
            playerCount = playerCount
        };
        this.InviteIPJoin(inviteIp, gsEnt, key);
    }

    private void ReBuildScanList()
    {
        int num = (this.mScanEndPort - this.mScanStartPort) + 1;
        for (int i = 0; i < num; i++)
        {
            int port = this.mScanStartPort + i;
            if ((this.ipList == null) || (this.ipList.Count == 0))
            {
                IPEndPoint curRemoteAddress = new IPEndPoint(IPAddress.Broadcast, port);
                this.DoAddToAddressList(port, curRemoteAddress);
            }
            else
            {
                TNet.List<string> ipList = this.ipList;
                lock (ipList)
                {
                    IEnumerator<string> enumerator = this.ipList.GetEnumerator();
                    try
                    {
                        while (enumerator.MoveNext())
                        {
                            string current = enumerator.Current;
                            IPEndPoint point2 = new IPEndPoint(IPAddress.Parse(current), port);
                            this.DoAddToAddressList(port, point2);
                        }
                    }
                    finally
                    {
                        if (enumerator == null)
                        {
                        }
                        enumerator.Dispose();
                    }
                }
            }
        }
    }

    public void RemoveIpFromRemoteList(string ip)
    {
        TNet.List<string> ipList = this.ipList;
        lock (ipList)
        {
            this.ipList.Remove(ip);
        }
        this.ReBuildScanList();
    }

    public void SetScanPortRange(int startPort, int endPort)
    {
        this.mScanStartPort = startPort;
        this.mScanEndPort = endPort;
        this.mRemoteAddressList.Clear();
        this.ReBuildScanList();
    }

    public void Start()
    {
        if (this.mLookupIPRequest == null)
        {
            this.mLookupIPRequest = TNet.Buffer.Create();
            this.mLookupIPRequest.BeginPacket((byte) 0x41).Write((ushort) 0x3600);
            this.mLookupIPRequest.EndPacket();
        }
        if (this.mRemoteAddressList.Count == 0)
        {
            this.ReBuildScanList();
        }
        int num = 3;
        for (int i = 0; i < num; i++)
        {
            if (this.mUdp.Start(Tools.randomPort))
            {
                break;
            }
        }
    }

    public void Stop()
    {
        this.isActive = false;
        this.errorString = string.Empty;
        this.knownServers.Clear();
        this.mCurInviteIP = null;
        this.mCurGS = null;
        this.mCurKey = string.Empty;
        this.mCurInviteSendTimes = 0;
        this.mNextInviteSend = 0L;
        try
        {
            this.mUdp.Stop();
            if (this.mLookupIPRequest != null)
            {
                this.mLookupIPRequest.Recycle();
                this.mLookupIPRequest = null;
            }
            if (this.onChange != null)
            {
                this.onChange();
            }
        }
        catch (Exception)
        {
        }
    }

    public void Update()
    {
        TNet.Buffer buffer;
        IPEndPoint point;
        bool flag = false;
        long time = DateTime.UtcNow.Ticks / 0x2710L;
        while ((this.mUdp != null) && this.mUdp.ReceivePacket(out buffer, out point))
        {
            if (buffer.size > 0)
            {
                try
                {
                    BinaryReader reader = buffer.BeginReading();
                    SPacket packet = (SPacket) reader.ReadByte();
                    switch (packet)
                    {
                        case SPacket.ResponseIp:
                        {
                            this.isActive = true;
                            this.mNextSend = time + this.SCAN_TIME;
                            ServerList.Entry newEntry = new ServerList.Entry();
                            newEntry.ReadFrom(reader);
                            if ((this.mSkipIPEP == null) || !newEntry.internalAddress.Equals(this.mSkipIPEP))
                            {
                                this.knownServers.Add(newEntry, time);
                                this.knownServers.Cleanup(time);
                                flag = true;
                            }
                            goto Label_0177;
                        }
                        case SPacket.ResponseJoin:
                            if (object.Equals(this.mCurInviteIP, point))
                            {
                                this.mCurInviteIP = null;
                                this.mCurGS = null;
                                this.mCurKey = string.Empty;
                                this.mCurInviteSendTimes = 0;
                            }
                            goto Label_0177;
                    }
                    if ((packet == SPacket.UserResponseJoin) && (reader.ReadUInt16() == 0x3600))
                    {
                        this.BeginSend(SPacket.ResponseUserResponseJoin);
                        this.EndSend(point);
                        bool accept = reader.ReadInt() == 1;
                        if (this.onResponseJoin != null)
                        {
                            this.onResponseJoin(point, accept);
                        }
                        if (object.Equals(this.mCurInviteIP, point))
                        {
                            this.mCurInviteIP = null;
                            this.mCurGS = null;
                            this.mCurKey = string.Empty;
                            this.mCurInviteSendTimes = 0;
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
        Label_0177:
            buffer.Recycle();
        }
        if (this.knownServers.Cleanup(time))
        {
            flag = true;
        }
        if (flag && (this.onChange != null))
        {
            this.onChange();
        }
        if ((this.mNextSend < time) && (this.mUdp != null))
        {
            this.mNextSend = time + this.SCAN_TIME;
            int count = this.mRemoteAddressList.Count;
            for (int i = 0; i < count; i++)
            {
                this.mUdp.Send(this.mLookupIPRequest, this.mRemoteAddressList[i]);
            }
        }
        if (((this.mNextInviteSend < time) && (this.mUdp != null)) && ((this.mCurInviteIP != null) && (this.mCurGS != null)))
        {
            if (this.mCurInviteSendTimes < 8)
            {
                TNet.Buffer buffer2 = TNet.Buffer.Create();
                BinaryWriter writer = buffer2.BeginPacket((byte) 0x43);
                writer.Write((ushort) 0x3600);
                this.mCurGS.WriteTo(writer);
                writer.Write(this.mCurKey);
                buffer2.EndPacket();
                this.mUdp.Send(buffer2, this.mCurInviteIP);
                buffer2.Recycle();
                buffer2 = null;
                this.mNextInviteSend = time + 0x7d0L;
                this.mCurInviteSendTimes++;
            }
            else
            {
                this.mCurInviteIP = null;
                this.mCurGS = null;
                this.mCurKey = string.Empty;
                this.mCurInviteSendTimes = 0;
            }
        }
    }

    public delegate void OnListChange();

    public delegate void OnResponseJoin(IPEndPoint inviteIp, bool accept);
}

